/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package GUI;

import java.awt.Cursor;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import modelo.Caixa;
import modelo.Cliente;

/**
 *
 * @author jean
 */
public class Simulacao extends javax.swing.JFrame {

    private Caixa caixa1 = new Caixa();
    private Caixa caixa2 = new Caixa();
    private Caixa caixa3 = new Caixa();
    private Caixa caixa4exp = new Caixa();
    private List<Cliente> filaCaixa1 = new ArrayList<Cliente>();
    private List<Cliente> filaCaixa2 = new ArrayList<Cliente>();
    private List<Cliente> filaCaixa3 = new ArrayList<Cliente>();
    private List<Cliente> filaCaixa4exp = new ArrayList<Cliente>();
    private Boolean termina = false;
    private Boolean pausa = false;
    private Integer minutoFila1 = 0;
    private Integer minutoFila2 = 0;
    private Integer minutoFila3 = 0;
    private Integer minutoFila4exp = 0;
    private Cliente cliente = new Cliente();

    /**
     * Creates new form simulacao
     */
    public Simulacao() {
        initComponents();
        textAndamento.setEnabled(false);
        btResumo.setEnabled(false);
        //btEncerrar.setEnabled(false);

        //Carrega as variáveis com os valores iniciais
        iniciaVariaveis();

        //Adiciona as pessoas iniciais em suas filas
        iniciaFilas();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        btCancelar = new javax.swing.JButton();
        btPausar = new javax.swing.JButton();
        btEncerrar = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        textAndamento = new javax.swing.JTextArea();
        btIniciar = new javax.swing.JButton();
        btResumo = new javax.swing.JButton();

        jLabel1.setText("jLabel1");

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        btCancelar.setText("Cancelar");
        btCancelar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btCancelarActionPerformed(evt);
            }
        });

        btPausar.setText("Pausar");
        btPausar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btPausarActionPerformed(evt);
            }
        });

        btEncerrar.setText("Encerrar");
        btEncerrar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btEncerrarActionPerformed(evt);
            }
        });

        textAndamento.setColumns(20);
        textAndamento.setRows(5);
        textAndamento.setDisabledTextColor(new java.awt.Color(1, 1, 1));
        jScrollPane1.setViewportView(textAndamento);

        btIniciar.setText("Iniciar Simulação");
        btIniciar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btIniciarActionPerformed(evt);
            }
        });

        btResumo.setText("Ver Resumo");
        btResumo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btResumoActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(btResumo)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 228, Short.MAX_VALUE)
                        .addComponent(btIniciar)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btPausar)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btEncerrar)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(btCancelar))
                    .addComponent(jScrollPane1))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGap(21, 21, 21)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 353, Short.MAX_VALUE)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btCancelar)
                    .addComponent(btPausar)
                    .addComponent(btEncerrar)
                    .addComponent(btIniciar)
                    .addComponent(btResumo))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void btCancelarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btCancelarActionPerformed
        this.setVisible(false);
    }//GEN-LAST:event_btCancelarActionPerformed

    private void btIniciarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btIniciarActionPerformed
        MouseLoading();

        if (btIniciar.getText().equalsIgnoreCase("Reiniciar Simulação")) {
            reinicia();
            textAndamento.append("Reiniciando.. Por favor aguarde... \n");
        } else {
            btIniciar.setEnabled(false);
            textAndamento.append("Iniciando.. Por favor aguarde... \n");
        }

        new Thread(new thAtualiza()).start();
    }//GEN-LAST:event_btIniciarActionPerformed

    private void btEncerrarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btEncerrarActionPerformed
        termina = true;
    }//GEN-LAST:event_btEncerrarActionPerformed

    private void btPausarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btPausarActionPerformed
        if (!pausa) {
            pausa = true;
            btPausar.setText("Continuar");
        } else {
            pausa = false;
            btPausar.setText("Pausar");
        }
    }//GEN-LAST:event_btPausarActionPerformed

    private void btResumoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btResumoActionPerformed
        MouseLoading();

        Resumo res = new Resumo(caixa1, caixa2, caixa3, caixa4exp);
        res.setTitle("Resumo");
        res.setLocationRelativeTo(null);
        res.setVisible(true);

        MouseWork();
    }//GEN-LAST:event_btResumoActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btCancelar;
    private javax.swing.JButton btEncerrar;
    private javax.swing.JButton btIniciar;
    private javax.swing.JButton btPausar;
    private javax.swing.JButton btResumo;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea textAndamento;
    // End of variables declaration//GEN-END:variables

    private void iniciaVariaveis() {
        caixa1.setTamanhoFila(4);
        caixa1.setTamanhoInicialFila(4);
        caixa1.setTempoMedioEspera(4);
        caixa1.setTamanhoMin(4);
        caixa1.setTamanhoMax(4);
        caixa1.setNumClientesAtendidos(0);
        caixa1.setEtiqueta("Caixa 1");

        caixa2.setTamanhoFila(3);
        caixa2.setTamanhoInicialFila(3);
        caixa2.setTempoMedioEspera(5);
        caixa2.setTamanhoMin(3);
        caixa2.setTamanhoMax(3);
        caixa2.setNumClientesAtendidos(0);
        caixa2.setEtiqueta("Caixa 2");

        caixa3.setTamanhoFila(2);
        caixa3.setTamanhoInicialFila(2);
        caixa3.setTempoMedioEspera(7);
        caixa3.setTamanhoMin(2);
        caixa3.setTamanhoMax(2);
        caixa3.setNumClientesAtendidos(0);
        caixa3.setEtiqueta("Caixa 3");

        caixa4exp.setTamanhoFila(4);
        caixa4exp.setTamanhoInicialFila(4);
        caixa4exp.setTempoMedioEspera(2);
        caixa4exp.setTamanhoMin(4);
        caixa4exp.setTamanhoMax(4);
        caixa4exp.setNumClientesAtendidos(0);
        caixa4exp.setEtiqueta("Caixa Expresso (Até 6 volumes)");
    }

    private void iniciaFilas() {
        for (int i = 0; i < caixa1.getTamanhoFila(); i++) {
            Cliente cli = new Cliente();
            filaCaixa1.add(cli);
        }

        for (int i = 0; i < caixa2.getTamanhoFila(); i++) {
            Cliente cli = new Cliente();
            filaCaixa2.add(cli);
        }

        for (int i = 0; i < caixa3.getTamanhoFila(); i++) {
            Cliente cli = new Cliente();
            filaCaixa3.add(cli);
        }

        for (int i = 0; i < caixa4exp.getTamanhoFila(); i++) {
            Cliente cli = new Cliente();
            filaCaixa4exp.add(cli);
        }
    }

    private void simulacao() {

        try {
            for (int i = 1; i <= 120; i++) {
                MouseLoading();
                textAndamento.append("\nMinuto da Simulação: " + i);
                //if (i == 10) {
                //btEncerrar.setEnabled(true);
                //}

                //Atualiza o minuto de atendimento
                atualizaFilas();

                //Atende um cliente conforme o tempo médio de espera
                atendeUm();

                //Chega um novo cliente
                cliente = new Cliente();

                //Gera um número aleatório de volumes para ele
                cliente.setQtdeVolumes(new Random().nextInt(50) == 0 ? 1 : new Random().nextInt(50));

                //Verifica em qual fila ele vai entrar
                Caixa caixa = verificaFila(cliente.getQtdeVolumes());

                String volume;
                if (cliente.getQtdeVolumes() == 1) {
                    volume = " Volume";
                } else {
                    volume = " Volumes";
                }

                textAndamento.append("\nCliente " + i + " entrará na fila do " + caixa.getEtiqueta() + " carregando " + cliente.getQtdeVolumes() + volume);

                adicionaNaFilaCerta(cliente, caixa);

                textAndamento.append("\nCliente aguardando na fila.");

                textAndamento.setCaretPosition(textAndamento.getDocument().getLength());

                textAndamento.append("\n");

                Thread.sleep(1000);

                while (pausa) {
                    MouseWork();
                    Thread.sleep(500);
                    if (termina) {
                        break;
                    }
                }

                if (termina) {
                    break;
                }
            }

            //Fim de simulação...
            encerra();
        } catch (InterruptedException ex) {
            Logger.getLogger(Simulacao.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void encerra() {

        textAndamento.append("\nFim...");

        btIniciar.setEnabled(true);
        btIniciar.setText("Reiniciar Simulação");
        if (btPausar.getText().equalsIgnoreCase("continuar")) {
            btPausar.setText("Pausar");
        }
        btPausar.setEnabled(false);
        btEncerrar.setEnabled(false);

        //Seta o número de clientes nas filas ao final da simulação
        caixa1.setNumClientesRestantes(filaCaixa1.size());
        caixa2.setNumClientesRestantes(filaCaixa2.size());
        caixa3.setNumClientesRestantes(filaCaixa3.size());
        caixa4exp.setNumClientesRestantes(filaCaixa4exp.size());

        //Habilita o botão para verificação dos resultados finais
        btResumo.setEnabled(true);

        textAndamento.setCaretPosition(textAndamento.getDocument().getLength());

        calculaMedias();

        MouseWork();
    }

    private void reinicia() {

        resetaVariaveis();

        textAndamento.setText("");
        btIniciar.setEnabled(false);
        btPausar.setEnabled(true);
        btEncerrar.setEnabled(true);
        btResumo.setEnabled(false);
    }

    private void resetaVariaveis() {
        caixa1 = new Caixa();
        caixa2 = new Caixa();
        caixa3 = new Caixa();
        caixa4exp = new Caixa();
        termina = false;
        pausa = false;

        iniciaVariaveis();
    }

    private void MouseLoading() {
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    }

    private void MouseWork() {
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }

    private Caixa verificaFila(Integer qtdeVolumes) {

        //Se a quantidade for menor ou igual a 6, já retorna o caixa expresso
        if (qtdeVolumes <= 6) {
            filaCaixa4exp.add(cliente);
            return caixa4exp;
        } //Se o Caixa 1 tiver o menor tamanho
        else if (caixa1.getTamanhoFila() < caixa2.getTamanhoFila() && caixa1.getTamanhoFila() < caixa3.getTamanhoFila()) {
            filaCaixa1.add(cliente);
            caixa1.setTamanhoFila(caixa1.getTamanhoFila() + 1);
            return caixa1;
        } //Se o Caixa 2 tiver o menor tamanho
        else if (caixa2.getTamanhoFila() < caixa1.getTamanhoFila() && caixa2.getTamanhoFila() < caixa3.getTamanhoFila()) {
            filaCaixa2.add(cliente);
            caixa2.setTamanhoFila(caixa2.getTamanhoFila() + 1);
            return caixa2;
        } //Se o Caixa 3 tiver o menor tamanho
        else if (caixa3.getTamanhoFila() < caixa1.getTamanhoFila() && caixa3.getTamanhoFila() < caixa2.getTamanhoFila()) {
            filaCaixa3.add(cliente);
            caixa3.setTamanhoFila(caixa3.getTamanhoFila() + 1);
            return caixa3;
        } //Se os Caixas 1 e 2 tiverem o mesmo tamanho e o 3 for maior
        else if (caixa1.getTamanhoFila() == caixa2.getTamanhoFila() && caixa3.getTamanhoFila() > caixa1.getTamanhoFila()) {
            //Verifica qual dos Caixas(1 e 2) tem o menor tempo medio de espera
            if (caixa1.getTempoMedioEspera() < caixa2.getTempoMedioEspera()) {
                filaCaixa1.add(cliente);
                caixa1.setTamanhoFila(caixa1.getTamanhoFila() + 1);
                return caixa1;
            } else {
                filaCaixa2.add(cliente);
                caixa2.setTamanhoFila(caixa2.getTamanhoFila() + 1);
                return caixa2;
            }
        } //Se os Caixas 2 e 3 tiverem o mesmo tamanho e o 1 for maior
        else if (caixa2.getTamanhoFila() == caixa3.getTamanhoFila() && caixa1.getTamanhoFila() > caixa2.getTamanhoFila()) {
            //Verifica qual dos Caixas(1 e 2) tem o menor tempo medio de espera
            if (caixa2.getTempoMedioEspera() < caixa3.getTempoMedioEspera()) {
                filaCaixa2.add(cliente);
                caixa2.setTamanhoFila(caixa2.getTamanhoFila() + 1);
                return caixa2;
            } else {
                filaCaixa3.add(cliente);
                caixa3.setTamanhoFila(caixa3.getTamanhoFila() + 1);
                return caixa3;
            }
        } //Se os Caixas 1 e 3 tiverem o mesmo tamanho e o 2 for maior
        else if (caixa1.getTamanhoFila() == caixa3.getTamanhoFila() && caixa2.getTamanhoFila() > caixa1.getTamanhoFila()) {
            //Verifica qual dos Caixas(1 e 3) tem o menor tempo medio de espera
            if (caixa1.getTempoMedioEspera() < caixa3.getTempoMedioEspera()) {
                filaCaixa1.add(cliente);
                caixa1.setTamanhoFila(caixa1.getTamanhoFila() + 1);
                return caixa1;
            } else {
                filaCaixa3.add(cliente);
                caixa3.setTamanhoFila(caixa2.getTamanhoFila() + 1);
                return caixa3;
            }
        } //Se todos os Caixas tiverem o mesmo tamanho de fila
        else if (caixa1.getTamanhoFila() == caixa2.getTamanhoFila() && caixa1.getTamanhoFila() == caixa3.getTamanhoFila()) {
            //Verifica o que tiver o menor tempo medio de atendimento
            if (caixa1.getTempoMedioEspera() < caixa2.getTempoMedioEspera() && caixa1.getTempoMedioEspera() < caixa3.getTempoMedioEspera()) {
                filaCaixa1.add(cliente);
                caixa1.setTamanhoFila(caixa1.getTamanhoFila() + 1);
                return caixa1;
            } else if (caixa2.getTempoMedioEspera() < caixa1.getTempoMedioEspera() && caixa2.getTempoMedioEspera() < caixa3.getTempoMedioEspera()) {
                filaCaixa2.add(cliente);
                caixa2.setTamanhoFila(caixa2.getTamanhoFila() + 1);
                return caixa2;
            } else if (caixa3.getTempoMedioEspera() < caixa1.getTempoMedioEspera() && caixa3.getTempoMedioEspera() < caixa2.getTempoMedioEspera()) {
                filaCaixa3.add(cliente);
                caixa3.setTamanhoFila(caixa3.getTamanhoFila() + 1);
                return caixa3;
            }
        }
        //Se cair aqui, algo deu errado!
        return null;
    }

    private void atendeUm() {
        if (caixa1.getMinuto() == 4) {

            //Atende mais um
            caixa1.setNumClientesAtendidos((caixa1.getNumClientesAtendidos()) + 1);

            //Remove da fila
            if (!filaCaixa1.isEmpty()) {
                filaCaixa1.remove(0);
                textAndamento.append("\nCaixa 1 atendeu um cliente em " + caixa1.getTempoMedioEspera() + " minutos");

                if (filaCaixa1.size() < caixa1.getTamanhoInicialFila()) {
                    caixa1.setTamanhoMin(filaCaixa1.size());
                }
            }

            //Verifica se está vazia e soma 1 a qtde de vezes vazia, caso esteja
            if (filaCaixa1.isEmpty()) {
                caixa1.setNumVezesVazia(caixa1.getNumVezesVazia() + 1);
            }

            //Recomeça a contagem
            minutoFila1 = 0;
        }

        if (caixa2.getMinuto() == 5) {

            //Atende mais um
            caixa2.setNumClientesAtendidos((caixa2.getNumClientesAtendidos()) + 1);

            //Remove da fila
            if (!filaCaixa2.isEmpty()) {
                filaCaixa2.remove(0);
                textAndamento.append("\nCaixa 2 atendeu um cliente em " + caixa2.getTempoMedioEspera() + " minutos");

                if (filaCaixa2.size() < caixa2.getTamanhoInicialFila()) {
                    caixa2.setTamanhoMin(filaCaixa2.size());
                }
            }

            //Verifica se está vazia soma 1 a qtde de vezes vazia, caso esteja
            if (filaCaixa2.isEmpty()) {
                caixa2.setNumVezesVazia(caixa2.getNumVezesVazia() + 1);
            }

            //Recomeça a contagem
            minutoFila2 = 0;
        }

        if (caixa3.getMinuto() == 7) {

            //Atende mais um
            caixa3.setNumClientesAtendidos((caixa3.getNumClientesAtendidos()) + 1);

            //Remove da fila
            if (!filaCaixa3.isEmpty()) {
                filaCaixa3.remove(0);
                textAndamento.append("\nCaixa 3 atendeu um cliente em " + caixa3.getTempoMedioEspera() + " minutos");

                if (filaCaixa3.size() < caixa3.getTamanhoInicialFila()) {
                    caixa3.setTamanhoMin(filaCaixa3.size());
                }
            }

            //Verifica se está vazia soma 1 a qtde de vezes vazia, caso esteja
            if (filaCaixa3.isEmpty()) {
                caixa3.setNumVezesVazia(caixa3.getNumVezesVazia() + 1);
            }

            //Recomeça a contagem
            minutoFila3 = 0;
        }

        if (caixa4exp.getMinuto() == 2) {

            //Atende mais um
            caixa4exp.setNumClientesAtendidos((caixa4exp.getNumClientesAtendidos()) + 1);

            //Remove da fila
            if (!filaCaixa4exp.isEmpty()) {
                filaCaixa4exp.remove(0);
                textAndamento.append("\nCaixa 4 atendeu um cliente em " + caixa4exp.getTempoMedioEspera() + " minutos");

                if (filaCaixa4exp.size() < caixa4exp.getTamanhoInicialFila()) {
                    caixa4exp.setTamanhoMin(filaCaixa4exp.size());
                }
            }

            //Verifica se está vazia soma 1 a qtde de vezes vazia, caso esteja
            if (filaCaixa4exp.isEmpty()) {
                caixa4exp.setNumVezesVazia(caixa4exp.getNumVezesVazia() + 1);
            }

            //Recomeça a contagem
            minutoFila4exp = 0;
        }
    }

    private void atualizaFilas() {
        minutoFila1++;
        minutoFila2++;
        minutoFila3++;
        minutoFila4exp++;

        caixa1.setMinuto(minutoFila1);
        caixa2.setMinuto(minutoFila2);
        caixa3.setMinuto(minutoFila3);
        caixa4exp.setMinuto(minutoFila4exp);

        caixa1.setNumMinutosProcessados((caixa1.getNumMinutosProcessados()) + 1);
        caixa2.setNumMinutosProcessados((caixa2.getNumMinutosProcessados()) + 1);
        caixa3.setNumMinutosProcessados((caixa3.getNumMinutosProcessados()) + 1);
        caixa4exp.setNumMinutosProcessados((caixa4exp.getNumMinutosProcessados()) + 1);
    }

    private void adicionaNaFilaCerta(Cliente cliente, Caixa caixa) {
        if (caixa.getEtiqueta().equals("Caixa 1")) {
            filaCaixa1.add(cliente);
            if (filaCaixa1.size() > caixa.getTamanhoMax()) {
                caixa1.setTamanhoMax(filaCaixa1.size());
            }
        } else if (caixa.getEtiqueta().equals("Caixa 2")) {
            filaCaixa2.add(cliente);
            if (filaCaixa2.size() > caixa.getTamanhoMax()) {
                caixa2.setTamanhoMax(filaCaixa2.size());
            }
        } else if (caixa.getEtiqueta().equals("Caixa 3")) {
            filaCaixa3.add(cliente);
            if (filaCaixa3.size() > caixa.getTamanhoMax()) {
                caixa3.setTamanhoMax(filaCaixa3.size());
            }
        } else if (caixa.getEtiqueta().equals("Caixa Expresso (Até 6 volumes)")) {
            filaCaixa4exp.add(cliente);
            if (filaCaixa4exp.size() > caixa.getTamanhoMax()) {
                caixa4exp.setTamanhoMax(filaCaixa4exp.size());
            }
        }
    }

    private void calculaMedias() {
        caixa1.setTempoMedioEspera(caixa1.getNumMinutosProcessados() / caixa1.getNumeroClientesAtendidos());
        caixa2.setTempoMedioEspera(caixa2.getNumMinutosProcessados() / caixa2.getNumeroClientesAtendidos());
        caixa3.setTempoMedioEspera(caixa3.getNumMinutosProcessados() / caixa3.getNumeroClientesAtendidos());
        caixa4exp.setTempoMedioEspera(caixa4exp.getNumMinutosProcessados() / caixa4exp.getNumeroClientesAtendidos());
    }

    class thAtualiza extends Thread {

        @Override
        public void run() {
            simulacao();
        }
    }
}
